package com.haole.ecommercepay.certificate;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.haole.ecommercepay.certificate.entity.CertificateItem;
import com.haole.ecommercepay.certificate.entity.EncryptedCertificateItem;
import com.haole.ecommercepay.certificate.entity.PlainCertificateItem;
import com.haole.ecommercepay.common.AesUtils;
import com.haole.ecommercepay.common.CertificateUtils;
import com.haole.ecommercepay.common.HttpUtils;
import com.haole.ecommercepay.common.SignUtils;
import com.haole.ecommercepay.ecommerce.url.EcommerceUrl;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

/**
 * @author haole
 */
public class Certificate {

    private static final Logger logger = LoggerFactory.getLogger(Certificate.class);

    /**
     * 获取微信支付平台证书
     *
     * @param merchantId        电商平台商户号
     * @param timeout           超时时间
     * @param serialNo          商户API证书序列号
     * @param mchPrivateKeyPath 商户私钥
     * @param APIv3Key          微信支付API v3密钥
     * @param savePath          微信支付平台证书保存路径
     * @return
     */
    public static List<X509Certificate> getCertByAPI(String merchantId, int timeout, String serialNo, String mchPrivateKeyPath, String wechatPubKeyPath, String APIv3Key, String savePath) {
        String result = "";
        //创建http请求
        HttpGet httpGet = new HttpGet(HttpUtils.API_URL_PREFIX.concat(EcommerceUrl.CERTIFICATES));
        httpGet.addHeader("Content-Type", "application/json");
        httpGet.addHeader("Accept", "application/json");

        String authorization = SignUtils.authorization("GET", EcommerceUrl.CERTIFICATES, merchantId, serialNo, "", mchPrivateKeyPath);

        //设置认证信息
        httpGet.setHeader("Authorization", authorization);

        //设置请求器配置:如超时限制等
        RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
        httpGet.setConfig(config);
        List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = httpClient.execute(httpGet);
            logger.info("下载平台证书返回结果===response：" + response);
            int statusCode = response.getStatusLine().getStatusCode();
            HttpEntity httpEntity = response.getEntity();
            result = EntityUtils.toString(httpEntity, "UTF-8");
            if (statusCode == 200) {
                logger.info("下载平台证书返回结果：" + result);
                Header[] timestampHeader = response.getHeaders("Wechatpay-Timestamp");
                Header[] nonceHeader = response.getHeaders("Wechatpay-Nonce");
                Header[] signatureHeader = response.getHeaders("Wechatpay-Signature");
                if (timestampHeader != null && timestampHeader.length > 0 &&
                        nonceHeader != null && nonceHeader.length > 0 &&
                        signatureHeader != null && signatureHeader.length > 0) {
                    // 验证微信支付返回签名
                    String wTimestamp = timestampHeader[0].getValue();
                    String wNonce = nonceHeader[0].getValue();
                    String wSign = signatureHeader[0].getValue();

                    logger.info("wTimestamp：{}，wNonce：{}，wSign：{}", wTimestamp, wNonce, wSign);
                    // 拼装待签名串
                    StringBuffer ss = new StringBuffer();
                    ss.append(wTimestamp).append("\n");
                    ss.append(wNonce).append("\n");
                    ss.append(result).append("\n");
                    // 验证签名
                    if (SignUtils.v3VerifyRSA(ss.toString(), Base64.decodeBase64(wSign.getBytes()), wechatPubKeyPath)) {
                        List<CertificateItem> certList = new ArrayList<CertificateItem>();
                        JSONObject json = JSONObject.parseObject(result);
                        logger.info("查询结果json字符串转证书List：" + json.get("data"));
                        JSONArray jsonArray = (JSONArray) json.get("data");
                        for (int i = 0; i < jsonArray.size(); i++) {
                            CertificateItem certificateItem = new CertificateItem();
                            EncryptedCertificateItem encryptCertificate = new EncryptedCertificateItem();
                            JSONObject bo = JSONObject.parseObject(jsonArray.get(i).toString());
                            certificateItem.setSerial_no(bo.get("serial_no").toString());
                            certificateItem.setEffective_time(bo.get("effective_time").toString());
                            certificateItem.setExpire_time(bo.get("expire_time").toString());
                            JSONObject encryptBo = JSONObject.parseObject(bo.get("encrypt_certificate").toString());
                            encryptCertificate.setAlgorithm(encryptBo.get("algorithm").toString());
                            encryptCertificate.setNonce(encryptBo.get("nonce").toString());
                            encryptCertificate.setAssociated_data(encryptBo.get("associated_data").toString());
                            encryptCertificate.setCiphertext(encryptBo.get("ciphertext").toString());
                            certificateItem.setEncrypt_certificate(encryptCertificate);
                            certList.add(certificateItem);
                        }
                        logger.info("证书List：" + certList);

                        List<PlainCertificateItem> plainList = decrypt(certList, APIv3Key);
                        if (CollectionUtils.isNotEmpty(plainList)) {
                            logger.info("平台证书开始保存");
                            x509Certs = saveCertificate(plainList, savePath);
                        }
                    }
                }
            }
            response.close();
            httpClient.close(); //throw
            return x509Certs;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("下载平台证书返回结果：" + e);
        }
        return x509Certs;
    }

    //证书保存
    private static List<X509Certificate> saveCertificate(List<PlainCertificateItem> cert, String savePath) throws IOException {
        File file = new File(savePath);
        deleteFile(file);
        List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
        file.mkdirs();
        for (PlainCertificateItem item : cert) {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(item.getPlainCertificate().getBytes(StandardCharsets.UTF_8));
            X509Certificate x509Cert = CertificateUtils.getCertificate(inputStream);
            x509Certs.add(x509Cert);
            String outputAbsoluteFilename = file.getAbsolutePath() + File.separator + "wechat_pay_platform_" + item.getSerialNo() + ".pem";
            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputAbsoluteFilename), StandardCharsets.UTF_8))) {
                writer.write(item.getPlainCertificate());
            }
            logger.info("输出证书文件目录：" + outputAbsoluteFilename);
        }
        return x509Certs;
    }

    private static List<PlainCertificateItem> decrypt(List<CertificateItem> certList, String APIv3Key) throws GeneralSecurityException, IOException {
        List<PlainCertificateItem> plainCertificateList = new ArrayList<PlainCertificateItem>();
        AesUtils aesUtil = new AesUtils(APIv3Key.getBytes(StandardCharsets.UTF_8));
        for (CertificateItem item : certList) {
            PlainCertificateItem bo = new PlainCertificateItem();
            bo.setSerialNo(item.getSerial_no());
            bo.setEffectiveTime(item.getEffective_time());
            bo.setExpireTime(item.getExpire_time());
            logger.info("平台证书密文解密");
            bo.setPlainCertificate(aesUtil.decryptToString(item.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
                    item.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8), item.getEncrypt_certificate().getCiphertext()));
            logger.info("平台证书公钥明文：" + bo.getPlainCertificate());
            plainCertificateList.add(bo);
        }
        return plainCertificateList;
    }

    public static void deleteFile(File file) {
        //判断文件不为null或文件目录存在
        if (file == null || !file.exists()) {
            logger.error("文件删除失败,请检查文件路径是否正确");
            return;
        }
        //取得这个目录下的所有子文件对象
        File[] files = file.listFiles();
        //遍历该目录下的文件对象
        for (File f : files) {
            //打印文件名
            String name = f.getName();
            logger.info(name);
            //判断子目录是否存在子目录,如果是文件则删除
            if (f.isDirectory()) {
                deleteFile(f);
            } else {
                f.delete();
            }
        }
        //删除空文件夹  for循环已经把上一层节点的目录清空。
        file.delete();
    }
}
